﻿using System;
using System.Windows;
using System.Windows.Media;

namespace LooseChart
{
    /// <summary>
    /// 时间段轴--单位：秒
    /// </summary>
    public class DurationAxis : IAxis
    {
        public DurationAxis() : base() { }

        public override double ValueToAxisValue(double value, ValueAxisConvertParamBase param)
        {
            return value;
        }

        public override double AxisValueToValue(double axisvalue, ValueAxisConvertParamBase param)
        {
            return axisvalue;
        }

        internal override void SetRange(double max, double min)
        {
            Range = new AxisRange() { Max = !double.IsNaN(max) && !double.IsInfinity(max) && Math.Ceiling(max) < TimeSpan.MaxValue.TotalSeconds ? max : Range.Max, Min = !double.IsNaN(min) && !double.IsInfinity(min) && Math.Floor(min) > TimeSpan.MinValue.TotalSeconds ? min : Range.Min };
        }

        public override void Draw()
        {
            if (!this.IsLoaded) return;
            var convertParam = GetConvertParam();
            int maxsubrangecount = Convert.ToInt32(convertParam.Length / (Math.Max(TimeSpan.FromSeconds(Math.Max(Math.Abs(Min), Math.Abs(Max))).Days.ToString("N0").Length, 3) * FontSize));
            if (maxsubrangecount <= 0) return;
            var subduration = (convertParam.MaxAxisValue - convertParam.MinAxisValue) / maxsubrangecount;
            double smallduration = 0d;
            string unit;
            if (subduration > 86400)
            {
                subduration = Math.Ceiling(subduration / 86400) * 86400;
                if (subduration > 86400) smallduration = GetSmallDuration(subduration, 3600, 6);
                else smallduration = 14400;
                unit = "d";
            }
            else if (subduration > 3600)
            {
                var hourduration = Convert.ToInt32(Math.Ceiling(subduration / 3600)) / 2 * 2;
                if (hourduration > 9) hourduration = 12;
                subduration = hourduration * 3600;
                if (subduration > 3600) smallduration = GetSmallDuration(subduration, 600, 6);
                else smallduration = 600;
                unit = "h";
            }
            else if (subduration > 60)
            {
                var minuteduration = Convert.ToInt32(Math.Ceiling(subduration / 60)) / 2 * 2;
                if (minuteduration > 25) minuteduration = 30;
                else if (minuteduration > 15) minuteduration = 20;
                else if (minuteduration > 7) minuteduration = 10;
                subduration = minuteduration * 60;
                if (subduration > 60) smallduration = GetSmallDuration(subduration, 10, 6);
                else smallduration = 10;
                unit = "m";
            }
            else
            {
                if (subduration > 30)
                {
                    subduration = Math.Ceiling(subduration / 30) * 30;
                    if (subduration > 30) smallduration = GetSmallDuration(subduration, 30, 6);
                }
                else if (subduration > 10)
                {
                    subduration = Math.Ceiling(subduration / 10) * 10;
                    if (subduration > 10) smallduration = GetSmallDuration(subduration, 10, 5);
                }
                else if (subduration > 5)
                {
                    subduration = Math.Ceiling(subduration / 5) * 5;
                    if (subduration > 5) smallduration = GetSmallDuration(subduration, 5, 5);
                }
                else if (subduration > 3)
                {
                    subduration = Math.Ceiling(subduration / 3) * 3;
                    if (subduration > 3) smallduration = GetSmallDuration(subduration, 3, 3);
                }
                else if (subduration > 2)
                {
                    subduration = Math.Ceiling(subduration / 2) * 2;
                    if (subduration > 2) smallduration = GetSmallDuration(subduration, 2, 2);
                }
                else
                {
                    smallduration = 1;
                    while (subduration < 1)
                    {
                        subduration *= 10;
                        smallduration /= 10;
                    }
                    subduration = 1;
                }
                unit = "s";
            }
            var begin = subduration == 1 ? Math.Floor(convertParam.MinAxisValue) : TimeSpan.FromSeconds(Math.Ceiling(convertParam.MinAxisValue / subduration) * subduration).TotalSeconds;
            var end = subduration == 1 ? Math.Ceiling(convertParam.MaxAxisValue) : TimeSpan.FromSeconds(Math.Floor(convertParam.MaxAxisValue / subduration) * subduration).TotalSeconds;
            using (var dc = DV.RenderOpen())
            {
                dc.DrawRectangle(Background, null, new Rect(0, 0, this.ActualWidth, this.ActualHeight));
                if (convertParam.AxisType == AxisType.X) dc.DrawLine(LinePen, new Point(0, 0), new Point(this.ActualWidth, 0));
                else if (convertParam.AxisType == AxisType.Y) dc.DrawLine(LinePen, new Point(this.ActualWidth, 0), new Point(this.ActualWidth, this.ActualHeight));
                else dc.DrawLine(LinePen, new Point(0, 0), new Point(0, this.ActualHeight));
                for (double i = begin; i <= end; i += subduration)
                {
                    TimeSpan time = TimeSpan.FromSeconds(i);
                    bool isuplevel = false;
                    string timestr;
                    switch (unit)
                    {
                        case "d":
                            timestr = string.Concat(time.Days, unit);
                            break;
                        case "h":
                            if (time.Hours != 0) timestr = string.Concat(time.Hours, unit);
                            else
                            {
                                timestr = string.Concat(time.Days, "d");
                                isuplevel = true;
                            }
                            break;
                        case "m":
                            if (time.Minutes != 0) timestr = string.Concat(time.Minutes, unit);
                            else
                            {
                                timestr = string.Concat(time.Hours, "h");
                                isuplevel = true;
                            }
                            break;
                        default:
                            if (time.Seconds != 0) timestr = string.Concat(time.Seconds, unit);
                            else
                            {
                                timestr = string.Concat(time.Minutes, "m");
                                isuplevel = true;
                            }
                            break;
                    }
                    DrawSeparate(dc, GetValueLocation(i, convertParam), timestr, isuplevel ? FontSize : PowerFontSize, isuplevel ? 8 : 5, convertParam);
                    if (smallduration > 0)
                    {
                        for (int j = 1; j < Convert.ToInt32(subduration / smallduration); j++) DrawSmallSeparate(dc, GetValueLocation(i + j * smallduration, convertParam), convertParam);
                    }
                }
            }
        }

        private double GetSmallDuration(double range, double smallrange, int maxcount)
        {
            var count = range < smallrange ? 1 : (range >= smallrange * int.MaxValue / 2 ? int.MaxValue / 2 : Convert.ToInt32(range / smallrange));
            if (count <= maxcount) return count * smallrange;
            int temp, max = count, min = maxcount;
            while (min != 0)
            {
                temp = max % min;
                max = min;
                min = temp;
            }
            if (max <= 0) return 0;
            var p = count / max;
            if (p > maxcount) return 0;
            return range / p;
        }

    }

}
